package com.guessyoulike.open.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.guessyoulike.open.utils.HttpProxyUtil;

import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * 登录授权登录
 *
 * @author guessyoulike.com
 * @date 2020/6/3 6:58 PM
 */
@Slf4j
@RestController
@RequestMapping(value = "/apple")
public class AppleController {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppleController.class);

    /**
     * 苹果登录apple sign in
     *
     * @param identityToken
     * @return
     */
    @RequestMapping(value = "/login/verify", method = RequestMethod.POST)
    public Map loginVerify(@RequestParam("identityToken") String identityToken) {
        try {
            Map<String, Object> appleUser = getAppleUserInfo(identityToken);
            if (appleUser == null) {
                return null;
            }
            LOGGER.info("AppleController.loginVerify user info : {}", appleUser);
            return appleUser;
        } catch (Exception e) {
            LOGGER.error("AppleController.loginVerify error:{}", e);
            return null;
        }
    }

    /**
     * 解密苹果用户信息
     *
     * @param identityToken
     * @return
     */
    private Map<String, Object> getAppleUserInfo(String identityToken) {
        try {
            String[] identityTokens = identityToken.split("\\.");
            Map<String, Object> data1 = JSONObject.parseObject(new String(Base64.decodeBase64(identityTokens[0]), "UTF-8"));
            Map<String, Object> data2 = JSONObject.parseObject(new String(Base64.decodeBase64(identityTokens[1]), "UTF-8"));
            String kid = (String) data1.get("kid");
            String aud = (String) data2.get("aud");
            String sub = (String) data2.get("sub");
            if (verify(identityToken, kid, aud, sub)) {
                Map<String, Object> data = new HashMap<>(2);
                data.put("data1", data1);
                data.put("data2", data2);
                return data;
            }
        } catch (Exception e) {
            LOGGER.error("AppleController.getAppleUserInfo error:{}", e);
        }
        return null;
    }

    /**
     * 验证token
     *
     * @param identityToken
     * @param aud
     * @param sub
     * @return
     */
    private boolean verify(String identityToken, String kid, String aud, String sub) {
        PublicKey publicKey = getPublicKey(kid);
        JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
        jwtParser.requireIssuer("https://appleid.apple.com");
        jwtParser.requireAudience(aud);
        jwtParser.requireSubject(sub);
        try {
            Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);
            String authKey = "auth_time";
            return claim != null && claim.getBody().containsKey(authKey);
        } catch (ExpiredJwtException e) {
            LOGGER.error("AppleController.verify identityToken expired:{}", e);
        } catch (Exception e) {
            LOGGER.error("AppleController.verify error:{}", e);
        }
        return false;
    }

    /**
     * 根据kid生成公钥
     *
     * @param kid
     * @return 构造好的公钥
     */
    private PublicKey getPublicKey(String kid) {
        try {
            String str = HttpProxyUtil.doGet("https://appleid.apple.com/auth/keys");
            JSONObject data = JSONObject.parseObject(str);
            JSONArray keysJsonArray = data.getJSONArray("keys");
            //n和e是应该是固定不变的，可以缓存起来，因为有时候从苹果服务器获取keys太慢了
            String n = "";
            String e = "";
            for (int i = 0; i < keysJsonArray.size(); i++) {
                JSONObject jsonObject = keysJsonArray.getJSONObject(i);
                if (StringUtils.equals(jsonObject.getString("kid"), kid)) {
                    n = jsonObject.getString("n");
                    e = jsonObject.getString("e");
                }
            }
            final BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
            final BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));

            final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            final KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePublic(spec);
        } catch (final Exception e) {
            LOGGER.error("AppleController.getPublicKey error:{}", e);
        }
        return null;
    }
}
